feat: add piece_count and piece_size fields to BomItem for cut-to-length parts - #12422
Conversation
…gth parts Manufacturing BOMs frequently require multiple pieces of a specific size cut from continuous stock (cables, tubing, structural profiles). Currently the only way to express "10 pieces of 250mm cable" is to enter the total length (2.5m) as quantity, which loses the piece-count information that purchasing and production need. This adds two optional fields to BomItem: - piece_count: number of discrete pieces required (default: 1) - piece_size: size/length of each piece (e.g. "250 mm") When piece_size is specified, the total quantity is auto-calculated as piece_count × piece_size, maintaining full backward compatibility (existing items effectively have piece_count=1 and empty piece_size). Changes: - Backend: new model fields, migration, updated recalculate_quantity() logic, hash_fields for BOM validation - API: serializer exposes piece_count and piece_size - Frontend: BOM form includes the new fields, BOM table shows them as optional columns Addresses inventree#10274
✅ Deploy Preview for inventree-web-pui-preview canceled.
|
|
@amanjain57-gif an interesting idea here, thanks for submitting. I think that you can achieve this with a single additional field, not two. If you remove the "piece_size" field and just use quantity, then the additional "piece_count" field can be used to reflect multiple pieces. e.g. a BOM with multiple lengths of wire
Thus the total requirement is 2m of wire, but we can see it is in 10x pieces of 200mm |
SchrodingersGat
left a comment
There was a problem hiding this comment.
Reduce to a single additional field as discussed
Remove the piece_size field entirely. The existing quantity field already represents the per-piece size/length, so piece_count multiplied by quantity gives the total material requirement. Example: quantity=200mm, piece_count=10 → total 2m of wire in 10 pieces. Changes: - Remove piece_size model field, serializer field, and frontend column/form - Update migration to only add piece_count - Update get_required_quantity() to multiply by piece_count - Restore original recalculate_quantity() without piece_size logic
|
Thanks for the feedback! Simplified to a single |
|
@amanjain57-gif thanks for the updates. Can you please also add some unit tests here, for (at least) the following:
|
|
You will also need to add comprehensive documentation for the new extension |
|
@SchrodingersGat Working on both requests:
Will push shortly. |
|
Done — pushed unit tests and documentation: Tests added (11 total):
Documentation:
|
|
Fixed |
|
@amanjain57-gif looking good! Please fix:
|
- Bump INVENTREE_API_VERSION to 531 with entry for piece_count field - Add CHANGELOG.md entry under Unreleased > Added - Fix RUF001: replace ambiguous × with x in serializers.py help_text
|
@SchrodingersGat Thanks — all three addressed:
The API Schema check should also resolve now, since the version bump lets the paths filter pick up the API change. |
|
@amanjain57-gif nice, this is looking pretty clean now |
|
Still need to address outstanding CI failures |
The 0153 AddField recorded help_text with a Unicode multiplication sign (×), while the model field uses plain 'x' after the RUF001 fix. This mismatch made makemigrations --check flag an unstaged 0154_alter_bomitem_piece_count migration, failing the DB test CI jobs. Update the original migration's help_text (and docstring) to plain 'x' so the field definition matches the model, keeping a single clean migration instead of add-then-alter.
|
@SchrodingersGat Fixed the outstanding CI failure. The DB test jobs were failing on the unstaged-migration check: Root cause: migration Since |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #12422 +/- ##
==========================================
- Coverage 86.83% 86.81% -0.03%
==========================================
Files 1449 1450 +1
Lines 96884 96982 +98
Branches 11185 11271 +86
==========================================
+ Hits 84132 84195 +63
- Misses 12688 12723 +35
Partials 64 64
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
BomItem.quantity is a derived field, recalculated from raw_amount on every save() via recalculate_quantity(). Setting item.quantity directly was overwritten back to the fixture value on save, so the tests computed against quantity=3 and failed. Use set_quantity() (which sets raw_amount) to match how quantity is meant to be updated.
|
Fixed the 5 failing Switched them to |
|
@amanjain57-gif you will need to address the conflict here due to API version updates |
…o-length-parts # Conflicts: # src/backend/InvenTree/InvenTree/api_version.py
|
@SchrodingersGat Conflict resolved — merged latest master. Upstream had taken |
|
Heads up on CI: the only red check is |
|
That pr is already in your branch; I am not sure how a rerun would help |
|
You're right — #12640 is already in the branch from the master merge, so a re-run wouldn't pull in anything new. My mistake referencing it; it also only touched What I actually meant is that the three failures ( I've pushed a fresh commit to trigger a clean run — let's see if they come back green. If you're seeing these as genuinely caused by this PR rather than environmental, I'd appreciate a pointer and I'll dig in; I can't reproduce Firefox E2E reliably locally. |
|
@amanjain57-gif please note that in future I have no interest in a discussion with your AI agent. |
* [bug] OptionalField race condition (inventree#12627) * [bug] OptionalField race condition Fixes subtle bug where OptionalField entries can be silenty dropped from an API request due to concurrent requests / race conditions * Additional unit tests * Additional guard in metadata.py * include extra kwargs * Adjust import/exporting options * Fix attribute sharing across class instances * reset errors on update (inventree#12550) * reset errors on update * add empty line --------- Co-authored-by: Oliver <oliver.henry.walters@gmail.com> * Add docs for return order (inventree#12632) * [bug] Fix ReturnOrder event registration (inventree#12633) * Allow bulk-edit for ReturnOrderLineItem (inventree#12635) * Allow bulk-edit for ReturnOrderLineItem - Allow bulk change of line item outcome * Update API version documentation with pull request links * [bug] Fix broken user setting (inventree#12638) - Closes inventree#12637 * try fixing this flaky test on firefox (inventree#12640) * feat: add piece_count and piece_size fields to BomItem for cut-to-length parts (inventree#12422) * feat: add piece_count and piece_size fields to BomItem for cut-to-length parts Manufacturing BOMs frequently require multiple pieces of a specific size cut from continuous stock (cables, tubing, structural profiles). Currently the only way to express "10 pieces of 250mm cable" is to enter the total length (2.5m) as quantity, which loses the piece-count information that purchasing and production need. This adds two optional fields to BomItem: - piece_count: number of discrete pieces required (default: 1) - piece_size: size/length of each piece (e.g. "250 mm") When piece_size is specified, the total quantity is auto-calculated as piece_count × piece_size, maintaining full backward compatibility (existing items effectively have piece_count=1 and empty piece_size). Changes: - Backend: new model fields, migration, updated recalculate_quantity() logic, hash_fields for BOM validation - API: serializer exposes piece_count and piece_size - Frontend: BOM form includes the new fields, BOM table shows them as optional columns Addresses inventree#10274 * refactor: simplify to single piece_count field per reviewer feedback Remove the piece_size field entirely. The existing quantity field already represents the per-piece size/length, so piece_count multiplied by quantity gives the total material requirement. Example: quantity=200mm, piece_count=10 → total 2m of wire in 10 pieces. Changes: - Remove piece_size model field, serializer field, and frontend column/form - Update migration to only add piece_count - Update get_required_quantity() to multiply by piece_count - Restore original recalculate_quantity() without piece_size logic * test/docs: add unit tests and documentation for piece_count field * style: replace ambiguous × with x to fix RUF002 lint error * Address review feedback: api_version bump, changelog, style fix - Bump INVENTREE_API_VERSION to 531 with entry for piece_count field - Add CHANGELOG.md entry under Unreleased > Added - Fix RUF001: replace ambiguous × with x in serializers.py help_text * fix: align piece_count migration help_text with model (RUF001) The 0153 AddField recorded help_text with a Unicode multiplication sign (×), while the model field uses plain 'x' after the RUF001 fix. This mismatch made makemigrations --check flag an unstaged 0154_alter_bomitem_piece_count migration, failing the DB test CI jobs. Update the original migration's help_text (and docstring) to plain 'x' so the field definition matches the model, keeping a single clean migration instead of add-then-alter. * fix: use set_quantity() in piece_count tests BomItem.quantity is a derived field, recalculated from raw_amount on every save() via recalculate_quantity(). Setting item.quantity directly was overwritten back to the fixture value on save, so the tests computed against quantity=3 and failed. Use set_quantity() (which sets raw_amount) to match how quantity is meant to be updated. * ci: re-trigger CI to confirm Firefox E2E failures are transient --------- Co-authored-by: Aman Jain <jainamn@amazon.com> * Improve error handling for unit registry (inventree#12643) * New Crowdin translations by GitHub Action (inventree#12625) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * chore(deps): bump the dependencies group across 1 directory with 6 updates (inventree#12651) Bumps the dependencies group with 6 updates in the /src/frontend directory: | Package | From | To | | --- | --- | --- | | [@codemirror/view](https://github.com/codemirror/view) | `6.43.7` | `6.43.8` | | [@sentry/react](https://github.com/getsentry/sentry-javascript) | `10.69.0` | `10.70.0` | | [react-hook-form](https://github.com/react-hook-form/react-hook-form) | `7.84.0` | `7.85.0` | | [styled-components](https://github.com/styled-components/styled-components) | `6.4.4` | `6.5.1` | | [@flakiness/playwright](https://github.com/flakiness/playwright) | `2.0.1` | `2.0.2` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.1.2` | `26.2.0` | Updates `@codemirror/view` from 6.43.7 to 6.43.8 - [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/view/commits) Updates `@sentry/react` from 10.69.0 to 10.70.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/10.70.0/CHANGELOG.md) - [Commits](getsentry/sentry-javascript@10.69.0...10.70.0) Updates `react-hook-form` from 7.84.0 to 7.85.0 - [Release notes](https://github.com/react-hook-form/react-hook-form/releases) - [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md) - [Commits](react-hook-form/react-hook-form@v7.84.0...v7.85.0) Updates `styled-components` from 6.4.4 to 6.5.1 - [Release notes](https://github.com/styled-components/styled-components/releases) - [Commits](https://github.com/styled-components/styled-components/compare/styled-components@6.4.4...styled-components@6.5.1) Updates `@flakiness/playwright` from 2.0.1 to 2.0.2 - [Release notes](https://github.com/flakiness/playwright/releases) - [Commits](flakiness/playwright@v2.0.1...v2.0.2) Updates `@types/node` from 26.1.2 to 26.2.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@codemirror/view" dependency-version: 6.43.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: "@sentry/react" dependency-version: 10.70.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: react-hook-form dependency-version: 7.85.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: styled-components dependency-version: 6.5.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: "@flakiness/playwright" dependency-version: 2.0.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: "@types/node" dependency-version: 26.2.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump the dependencies group with 5 updates (inventree#12652) Bumps the dependencies group with 5 updates: | Package | From | To | | --- | --- | --- | | [dorny/paths-filter](https://github.com/dorny/paths-filter) | `4.0.2` | `4.0.3` | | [oasdiff/oasdiff-action/diff](https://github.com/oasdiff/oasdiff-action) | `0.1.11` | `0.1.12` | | [CodSpeedHQ/action](https://github.com/codspeedhq/action) | `5.0.2` | `5.0.3` | | [actions/attest](https://github.com/actions/attest) | `4.2.1` | `4.2.2` | | [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.37.5` | `4.37.6` | Updates `dorny/paths-filter` from 4.0.2 to 4.0.3 - [Release notes](https://github.com/dorny/paths-filter/releases) - [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md) - [Commits](dorny/paths-filter@7b450ff...ceb8a2b) Updates `oasdiff/oasdiff-action/diff` from 0.1.11 to 0.1.12 - [Release notes](https://github.com/oasdiff/oasdiff-action/releases) - [Commits](oasdiff/oasdiff-action@b7c3ade...033c15c) Updates `CodSpeedHQ/action` from 5.0.2 to 5.0.3 - [Release notes](https://github.com/codspeedhq/action/releases) - [Changelog](https://github.com/CodSpeedHQ/action/blob/main/CHANGELOG.md) - [Commits](CodSpeedHQ/action@0ca9cbb...4296e51) Updates `actions/attest` from 4.2.1 to 4.2.2 - [Release notes](https://github.com/actions/attest/releases) - [Changelog](https://github.com/actions/attest/blob/main/RELEASE.md) - [Commits](actions/attest@508db95...1e69f48) Updates `github/codeql-action/upload-sarif` from 4.37.5 to 4.37.6 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@d1ba80a...5595cca) --- updated-dependencies: - dependency-name: dorny/paths-filter dependency-version: 4.0.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: oasdiff/oasdiff-action/diff dependency-version: 0.1.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: CodSpeedHQ/action dependency-version: 5.0.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: actions/attest dependency-version: 4.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.37.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump the dependencies group across 1 directory with 14 updates (inventree#12642) * chore(deps): bump the dependencies group across 1 directory with 14 updates Bumps the dependencies group with 14 updates in the /src/backend directory: | Package | From | To | | --- | --- | --- | | [cffi](https://github.com/python-cffi/cffi) | `2.1.0` | `2.1.1` | | [coverage](https://github.com/coveragepy/coveragepy) | `7.15.2` | `7.15.4` | | [django-stubs](https://github.com/typeddjango/django-stubs) | `6.0.7` | `6.0.9` | | [django-stubs-ext](https://github.com/typeddjango/django-stubs) | `6.0.6` | `6.0.9` | | [django-test-migrations](https://github.com/wemake-services/django-test-migrations) | `1.5.0` | `1.6.0` | | [packaging](https://github.com/pypa/packaging) | `26.2` | `26.3` | | [pip](https://github.com/pypa/pip) | `26.2` | `26.2.1` | | [prek](https://github.com/j178/prek) | `0.4.11` | `0.4.12` | | [pytest-django](https://github.com/pytest-dev/pytest-django) | `4.12.0` | `4.13.0` | | [blessed](https://github.com/jquast/blessed) | `1.47.0` | `1.48.0` | | [boto3](https://github.com/boto/boto3) | `1.43.61` | `1.43.66` | | [botocore](https://github.com/boto/botocore) | `1.43.61` | `1.43.66` | | [djangorestframework](https://github.com/encode/django-rest-framework) | `3.17.1` | `3.18.0` | | [googleapis-common-protos](https://github.com/googleapis/google-cloud-python) | `1.75.0` | `1.75.1` | Updates `cffi` from 2.1.0 to 2.1.1 - [Release notes](https://github.com/python-cffi/cffi/releases) - [Commits](python-cffi/cffi@v2.1.0...v2.1.1) Updates `coverage` from 7.15.2 to 7.15.4 - [Release notes](https://github.com/coveragepy/coveragepy/releases) - [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst) - [Commits](coveragepy/coveragepy@7.15.2...7.15.4) Updates `django-stubs` from 6.0.7 to 6.0.9 - [Release notes](https://github.com/typeddjango/django-stubs/releases) - [Commits](typeddjango/django-stubs@6.0.7...6.0.9) Updates `django-stubs-ext` from 6.0.6 to 6.0.9 - [Release notes](https://github.com/typeddjango/django-stubs/releases) - [Commits](typeddjango/django-stubs@6.0.6...6.0.9) Updates `django-test-migrations` from 1.5.0 to 1.6.0 - [Release notes](https://github.com/wemake-services/django-test-migrations/releases) - [Changelog](https://github.com/wemake-services/django-test-migrations/blob/master/CHANGELOG.md) - [Commits](wemake-services/django-test-migrations@1.5.0...1.6.0) Updates `packaging` from 26.2 to 26.3 - [Release notes](https://github.com/pypa/packaging/releases) - [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst) - [Commits](pypa/packaging@26.2...26.3) Updates `pip` from 26.2 to 26.2.1 - [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst) - [Commits](pypa/pip@26.2...26.2.1) Updates `prek` from 0.4.11 to 0.4.12 - [Release notes](https://github.com/j178/prek/releases) - [Changelog](https://github.com/j178/prek/blob/master/CHANGELOG.md) - [Commits](j178/prek@v0.4.11...v0.4.12) Updates `pytest-django` from 4.12.0 to 4.13.0 - [Release notes](https://github.com/pytest-dev/pytest-django/releases) - [Changelog](https://github.com/pytest-dev/pytest-django/blob/main/docs/changelog.rst) - [Commits](pytest-dev/pytest-django@v4.12.0...v4.13.0) Updates `blessed` from 1.47.0 to 1.48.0 - [Release notes](https://github.com/jquast/blessed/releases) - [Changelog](https://github.com/jquast/blessed/blob/master/docs/history.rst) - [Commits](https://github.com/jquast/blessed/commits/1.48) Updates `boto3` from 1.43.61 to 1.43.66 - [Release notes](https://github.com/boto/boto3/releases) - [Commits](boto/boto3@1.43.61...1.43.66) Updates `botocore` from 1.43.61 to 1.43.66 - [Commits](boto/botocore@1.43.61...1.43.66) Updates `djangorestframework` from 3.17.1 to 3.18.0 - [Release notes](https://github.com/encode/django-rest-framework/releases) - [Commits](encode/django-rest-framework@3.17.1...3.18.0) Updates `googleapis-common-protos` from 1.75.0 to 1.75.1 - [Release notes](https://github.com/googleapis/google-cloud-python/releases) - [Changelog](https://github.com/googleapis/google-cloud-python/blob/main/packages/google-cloud-documentai/CHANGELOG.md) - [Commits](googleapis/google-cloud-python@googleapis-common-protos-v1.75.0...googleapis-common-protos-v1.75.1) --- updated-dependencies: - dependency-name: cffi dependency-version: 2.1.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: coverage dependency-version: 7.15.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: django-stubs dependency-version: 6.0.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: django-stubs-ext dependency-version: 6.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: django-test-migrations dependency-version: 1.6.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: packaging dependency-version: '26.3' dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: pip dependency-version: 26.2.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: prek dependency-version: 0.4.12 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: pytest-django dependency-version: 4.13.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: blessed dependency-version: 1.48.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: boto3 dependency-version: 1.43.66 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: botocore dependency-version: 1.43.66 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: djangorestframework dependency-version: 3.18.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: googleapis-common-protos dependency-version: 1.75.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies ... Signed-off-by: dependabot[bot] <support@github.com> * fix style --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Matthias Mair <code@mjmair.com> * Fix issue 10769 (inventree#12286) * fix: Show badges for flagged stock items * Updated badge logic to only display on build order and sales order * refactor: Remove unused modelRenderer for RenderStockItem in BuildForms and SalesOrderForms * [FR] stop shipping docs with packages Fixes inventree#12610 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Oliver <oliver.henry.walters@gmail.com> Co-authored-by: ribsey <lukas.ribi@gmail.com> Co-authored-by: amanjain57-gif <aman.jain57@gmail.com> Co-authored-by: Aman Jain <jainamn@amazon.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alonso Lopez-Valdez <alonsl1@uci.edu>
Summary
Addresses #10274
Manufacturing BOMs frequently require multiple pieces of a specific size cut from continuous stock (cables, tubing,
structural profiles). Currently the only way to express "10 pieces of 250mm cable" is to enter the total length (2.5m)
as quantity, which loses the piece-count information that purchasing and production need.
Changes
Adds two optional fields to BomItem:
When
piece_sizeis specified, the total quantity is auto-calculated aspiece_count × piece_size, maintaining fullbackward compatibility (existing items effectively have piece_count=1 and empty piece_size).
Backend
recalculate_quantity()to compute total from piece_count × piece_size when piece_size is setAPI
piece_countandpiece_sizefieldsFrontend
Example
A BOM line for "10 pieces of 250mm aluminum profile" (part units: m):
This preserves the per-piece information for production while correctly computing total material requirement for
purchasing.